

local ToggleOnNextStrike = {
	setup = {
		traceLevel = 0;
	};
};

-- Contains the list of abilities to patch (or rather, the macros to change) and the ability to add / remove.
-- TODO: replace the abilities with GetSpellInfo references later
local ABILITIES = {
	["DEATHKNIGHT"] = {
		abilityList = {
			49930; --"Blood Strike";
			49921; --"Plague Strike";
			49909; --"Icy Touch";
			49924; --"Death Strike";

			55268; --"Frost Strike";
			55262; --"Heart Strike";
			55271; --"Scourge Strike";
		};
		ability = 56815; --"Rune Strike"; 
	};
	["DRUID"] = {
		abilityList = {
			48564; --"Mangle - Bear";
			48562; --"Swipe";
			48568; --"Lacerate";
		};
		ability = 48480; --"Maul"; 
	};
	["WARRIOR"] = {
		abilityList = {
			47498; --"Devastate";
			47467; --"Sunder Armor";
		};
		ability = 47450; --"Heroic Strike"; 
	};
};

function ToggleOnNextStrike_Get_Instance()
	return ToggleOnNextStrike;
end


function ToggleOnNextStrike:Init()
	local class;
	_ , class = UnitClass("player")
	if type(ABILITIES[class]) == "table" then
		for k, v in pairs(ABILITIES[class]) do
			self.setup[k] = v
		end
	end
	if type(self.setup.abilityList) == "table" then
		for k, v in pairs(self.setup.abilityList) do
			if type(v) == "number" then
				local n = GetSpellInfo(v)
				if type(n) ~= nil then
					self.setup.abilityList[k] = n
				end
			end
		end
	end
	if type(self.setup.ability) == "number" then
		local n = GetSpellInfo(self.setup.ability)
		if type(n) ~= nil then
			self.setup.ability = n
		end
	end
end

function ToggleOnNextStrike:Message(msg)
	ChatFrame1:AddMessage(msg, 1, 1, 0.1);
end

function ToggleOnNextStrike:WarnOnce(msg)
	if type(self.setup.warnings) ~= "table" then
		self.setup.warnings = {};
	end
	if not self.setup.warnings[msg] then
		self.setup.warnings[msg] = true
		self:Error(msg)
	end
end

function ToggleOnNextStrike:Error(error)
	ChatFrame1:AddMessage(error, 1, 0.1, 0.1);
end

function ToggleOnNextStrike:Trace(traceLevel, error)
	if ( type(traceLevel) == "number" ) and ( self.setup.traceLevel >= traceLevel ) then
		ChatFrame2:AddMessage(error, 1, 0.1, 0.1);
	end
end

function ToggleOnNextStrike:ChangeMacro(macroText,useNextStrike,ability)
	if type(macroText) ~= "string" then
		self:Error("TONS:ChangeMacro - received erroneous macroText parameter")
		return macroText
	end
	if type(ability) ~= "string" then
		self:Error("TONS:ChangeMacro - received erroneous ability parameter")
		return macroText
	end
	local castStr = ToggleOnNextStrike.L("/cast")
	local str = string.format(castStr.." !%s", ability)
	if useNextStrike then
		if not string.find(macroText, str, 1, true) then
			if ( string.sub(macroText, strlen(macroText)) ~= "\n" ) then
				return macroText.."\n"..str;
			else
				return macroText..str;
			end
		end
	else
		local index = string.find(macroText, str, 1, true)
		if index then
			self:Trace(4, "TONS: Found ability in macro");
			local endIndex = index+strlen(str)
			local tmp = string.sub(macroText, 1, index-1)
			self:Trace(5, "TONS: shortening macro to "..tmp);
			if (string.sub(macroText, endIndex, endIndex) == "\n" ) then
				self:Trace(5, "TONS: increasing end index by one due to newline");
				endIndex = endIndex + 1
			end
			if endIndex <= strlen(str) then
				self:Trace(5, "TONS: adding stuff after ability");
				tmp = tmp..string.sub(macroText, endIndex)
			end
			self:Trace(5, "TONS: end result - "..tmp);
			return tmp
		end
	end
	return macroText
end

function ToggleOnNextStrike:GetMaximumMacroId()
	-- return 54
	local i = 54
	local n = GetMacroInfo(i)
	if n ~= nil then
		while ( n ~= nil ) do
			i = i + 1
			n = GetMacroInfo(i)
		end
	else
		while ( n == nil ) do
			i = i - 1
			n = GetMacroInfo(i)
		end
	end
	return i
end

function ToggleOnNextStrike:FindMacroId(macroName)
	local i = GetMacroIndexByName(macroName)
	if not i then
		-- TODO: Change this to iterate through all macros.
		-- GetNumMacros(); and use MAX_ACCOUNT_MACROS = 36
		for i = self:GetMaximumMacroId(), 1, -1 do
			local n=GetMacroInfo(i) 
			if type(n) == "string" and n == macroName then
				return i
			end
		end
	end
	return i
end

function ToggleOnNextStrike:GetMacroList()
	local macroList = self:GetSetting("macroList")
	if macroList == nil then macroList = {} end
	return macroList
end

function ToggleOnNextStrike:PerformMacroChange(useNextStrike,ability)
	if InCombatLockdown() then
		self:Error("TONS: Could not change macros since we were in combat.")
		return
	end
	local t = "true"
	if not useNextStrike then t = "false" end
	self:Trace(5, "TONS: current use next strike value - "..t);
	local changes = 0
	local macroList = self:GetMacroList()
	if type(macroList) == "table" then
		for k, v in pairs(macroList) do
			local i=self:FindMacroId(k)
			if i then
				local mt=GetMacroBody(i)
				if type(MacroFrame) ~= "nil" and MacroFrame.selectedMacro == i then
					if type(MacroFrameText) ~= nil and type(MacroFrameText.GetText) == "function" then
						mt = MacroFrameText:GetText()
					end
				end
				self:Trace(5, "TONS: current macro text - "..mt);
				local newText = self:ChangeMacro(mt, useNextStrike, ability)
				self:Trace(5, "TONS: new macro text - "..newText);
				if strlen(newText) <= 255 then
					local n,t,_,l=GetMacroInfo(i)
					if type(MacroFrame) ~= "nil" and MacroFrame.selectedMacro == i then
						if type(MacroFrameText) ~= nil and type(MacroFrameText.SetText) == "function" then
							MacroFrameText:SetText(newText)
						end
					end
					EditMacro(i,n,t,newText,l,1)
					changes = changes + 1
				else
					self:WarnOnce(string.format("TONS:PerformMacroChange - Can not change macro '%s' due to resulting macro exceeding 255 characters!", v))
				end
			else
				self:Trace(2, string.format("TONS:PerformMacroChange - Could not find macro '%s'.", v));
			end
		end
	end
	return changes
end

-- This is the function that should be called in the key binding.
-- It can also be called by /run ToggleOnNextStrike_Get_Instance():PerformToggle()
function ToggleOnNextStrike:PerformToggle(newMode)
	if not InCombatLockdown() then
		if type(self.setup.ability) ~= "string" then
			self:Error("TONS: Current class does not have an On Next Strike ability mapped to it.")
			return
		end
		if type(newMode) == "nil" then
			self:SetMode(not self:GetMode())
		else
			self:SetMode(newMode)
		end
		self:PerformMacroChange(self:GetMode(), self.setup.ability)
		local verb = "include"
		if ( not self:GetMode() ) then
			verb = "exclude"
		end
		self:Message(string.format("TONS: Changed macros to %s %s.", verb, self.setup.ability))
		return
	else
		self:Trace(3, "TONS: Can not change macros during combat")
	end
end

ToggleOnNextStrike:Init()